﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Threading;

public interface ISocketServer
{
    void OnReceiveData(EndPoint endPoint, byte[] data);
    void OnRecOrSendFailed(EndPoint endPoint);
}

public class UDPSocketServer : ServerSocket
{
    EndPoint m_ListenEndPoint;
    public EndPoint endPoint
    {
        get => m_ListenEndPoint;
        set => m_ListenEndPoint = value;
    }

    public override bool connected => true;

    ISocketServer m_ISocketServer;

    public override ISocketServer socketServerListenr
    {
        get { return m_ISocketServer; }
    }

    const byte connectByte = 9;

    public UDPSocketServer()
    {
        m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        //m_Socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
    }
    
    public UDPSocketServer(ISocketServer ISocketServer)
    {
        m_Socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);

        SetSocketServerHandler(ISocketServer);
    }
    
    public void SetSocketServerHandler(ISocketServer ISocketServer)
    {
        this.m_ISocketServer = ISocketServer;
    }

    public override void Start(int port,int maxConnectCount)
    {
        //绑定端口 
        var anyIPEndPoint = new IPEndPoint(IPAddress.Any, port);

        m_ListenEndPoint = anyIPEndPoint;

        m_Socket.Bind(anyIPEndPoint);
        
        GameDebug.Log($"UDPSocketServer Start Bind:{m_ListenEndPoint}");
    }
    public override void BeginReceive()
    {
        if (m_Socket == null)
        {
            GameDebug.LogError("BeginReceive m_Socket == null");
            return;
        }

        EndPoint receiveEndPoint = new IPEndPoint(IPAddress.Any, 0);
        try
        {
            m_Socket.BeginReceiveFrom(m_Buffer, 0, m_Buffer.Length, 0, ref receiveEndPoint, OnReceiveData, null);
            
        }
        catch (Exception e)
        {
            GameDebug.LogError($"BeginReceive：{receiveEndPoint} 出错");
            LogSocketException(e);
            

            Disconnect(receiveEndPoint);
        }
    }

    /// <summary>
    /// 接收到数据
    /// </summary>
    /// <param name="result"></param>
    void OnReceiveData(IAsyncResult result)
    {
        if (m_Socket == null )
        {
            GameDebug.LogError("OnReceiveData m_Socket == null");
            return;
        }

        EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
        try
        {
            int receiveCount = m_Socket.EndReceiveFrom(result, ref remoteEndPoint);
            if (receiveCount == 0) // 连接已关闭
            {
                GameDebug.Log("收到关闭信号");
                this.Disconnect(remoteEndPoint);
            }
            else
            {
                if (receiveCount == 1)
                {
                    GameDebug.Log($"有客户端Ping回答:{remoteEndPoint} ");

                    this.m_ISocketServer?.OnReceiveData(remoteEndPoint, new byte[1] { m_Buffer[0] });

                    //sockerServerListener?.OnNewConnected(remoteEndPoint, null);
                }
                else
                {
                    byte[] newData = new byte[receiveCount];

                    Array.Copy(m_Buffer, newData, receiveCount);

                    GameDebug.Log($"收到{remoteEndPoint}的数据  socket:{this.m_Socket.GetHashCode()} 长度：{receiveCount} ");
                    
                    List<byte[]> toProcessData = TryGetMessageWithReceiveData(newData);

                    toProcessData.ForEach(e =>
                    {
                        this.m_ISocketServer?.OnReceiveData(remoteEndPoint, e);
                    });
                }
            }

            BeginReceive();
        }
        catch (Exception ex)
        {
            GameDebug.LogError($"接收：{remoteEndPoint} 出错 ");
            
            LogSocketException(ex);

            Disconnect(remoteEndPoint);
        }
    }

    private List<UDPPacket> m_UDPPackets = new List<UDPPacket>();

    protected override List<byte[]> TryGetMessageWithReceiveData(byte[] newData)
    {
        List<byte[]> res = new List<byte[]>();

        int id = BitConverter.ToInt32(newData, 0);

        UDPPacket packet = m_UDPPackets.Find(e => e.id == id);
        if (packet == null)
        {
            UDPPacket newPacket = new UDPPacket(id);

            newPacket.OnReceivePacket(newData);

            if (newPacket.receiveEnd)
            {
                //GameDebug.Log($"新包处理数据：{newPacket.contentLength}");
                res.Add(newPacket.data);
            }
            else
            {
                m_UDPPackets.Add(newPacket);
            }
        }
        else
        {
            packet.OnReceivePacket(newData);

            if (packet.receiveEnd)
            {
                res.Add(packet.data);
                GameDebug.LogError($"接收包完成");
                m_UDPPackets.Remove(packet);
            }
        }

        return res;
    }

    //public void TestSend(EndPoint m_RemoteEndPoint)
    //{
    //    byte[] data = new byte[] { 1, 1, 1, 1, 1, 1, 1, 2 };
    //    int ret = m_Socket.SendTo(data, m_RemoteEndPoint);

    //    GameDebug.Log($"发送UDP数据:{ret}  m_Socket.Connected:{m_Socket.Connected}  m_RemoteEndPoint:{m_RemoteEndPoint}");
    //}

    const int maxLengthSendATime = UDPPacket.maxSendContentLengthOneTime;

    private int m_Seq;
    protected int GetCurSeqId()
    {
        return System.Threading.Interlocked.Increment(ref m_Seq);
    }
    /// <summary>
    /// 发送数据
    /// </summary>
    /// <param name="client"></param>
    /// <param name="packet"></param>
    public override void SendPacket(byte[] packet, EndPoint endPoint)
    {
        int packetId = GetCurSeqId();

        byte[] bytes = new byte[packet.Length];
        System.Array.Copy(packet, bytes, packet.Length);

        Queue<byte[]> m_SendQueue = new Queue<byte[]>();

        int index = 0;
        while (bytes.Length > maxLengthSendATime)
        {
            byte[] block = new byte[maxLengthSendATime];
            Array.Copy(bytes, block, maxLengthSendATime);

            m_SendQueue.Enqueue(block);

            index += maxLengthSendATime;

            bytes = new byte[bytes.Length - maxLengthSendATime];
            Array.Copy(packet, index, bytes, 0, bytes.Length);
        }

        m_SendQueue.Enqueue(bytes);

        int sendIndex = 0;
        while (m_SendQueue.Count > 0)
        {
            byte[] sendData = m_SendQueue.Dequeue();

            byte[] headBytes = BitConverter.GetBytes(packetId);
            byte[] indexBytes = BitConverter.GetBytes(sendIndex);

            string tail = "----";
            byte[] tailBytes = System.Text.Encoding.UTF8.GetBytes(tail);

            int dataLength = sendData.Length;
            int headCount = headBytes.Length;
            int indexCount = indexBytes.Length;
            int tailCount = tailBytes.Length;

            if (m_SendQueue.Count == 0)
            {
                tail = UDPPacket.Tail;
                tailBytes = System.Text.Encoding.UTF8.GetBytes(tail);

                //GameDebug.Log($"发送尾包");
            }

            byte[] data = new byte[headCount + dataLength + indexCount + tailCount];

            System.Array.Copy(headBytes, 0, data, 0, headCount);
            System.Array.Copy(indexBytes, 0, data, headCount, indexCount);
            System.Array.Copy(sendData, 0, data, headCount + indexCount, dataLength);
            System.Array.Copy(tailBytes, 0, data, headCount + indexCount + dataLength, tailCount);

            GameDebug.LogError($"发送 sendIndex:{sendIndex} total:{data.Length} packetHeadCount:{headCount}  indexCount:{indexCount}  dataLength:{data.Length}  tailCount：{tailCount}  endPoint:{endPoint}  ");

            this._SendData(data, endPoint);

            Thread.Sleep(10);

            sendIndex++;
        }
    }


    public override void _SendData(byte[] data, EndPoint endPoint)
    {
        if (m_Socket == null)
        {
            GameDebug.LogError($"m_Socket null");
            return;
        }

        try
        {
            m_Socket.SendTo(data, endPoint);

            //GameDebug.Log($"m_Socket SendTo：{data.Length}");
        }
        catch (Exception e)
        {
            GameDebug.LogError($"_SendData：{endPoint} 出错");
            GameDebug.LogError(e.Message);
        }
    }
    protected override void Disconnect()
    {
        //Disconnect(this.endPoint);
    }

    void Disconnect(EndPoint endPoint)
    {
        lock (_packetBaton)
        {
            m_Packet = null;
        }
        this.m_ISocketServer?.OnRecOrSendFailed(endPoint);
    }

    public override void Release()
    {
        base.Release();

        try
        {
            Socket s = m_Socket;
            m_Socket = null;
            if(s.Connected)
                s.Shutdown(SocketShutdown.Both);
            s.Close();

        }
        catch (Exception e)
        {
            GameDebug.LogError(e.Message+"/n"+e.StackTrace);
        }
    }

    //public static string GetIp
    //{
    //    get
    //    {
    //        var localIp = "";
    //        var host = Dns.GetHostEntry(Dns.GetHostName());
    //        foreach (var ip in host.AddressList) { if (ip.AddressFamily.ToString() == "InterNetwork") localIp = ip.ToString(); }
    //        return localIp;
    //    }

    //}
}

